#include "PlayState.h"
#include "NetworkState.h"
#include "PauseState.h"

using namespace Ogre;
using namespace NxOgre;

PlayState PlayState::mPlayState;

PlayState::PlayState() :
	mRoot(0), mSceneMgr(0), mViewport(0), mDeviceManager(0), mGUIManager(0), mKeyboard(0), mMouse(0), mCamera(0),
	mDotSceneLoader(0),
	mChatBox(0), mSpriteManager(0),
	mQuit(false), mDismiss(false),
	nxWorld(0), nxScene(0),
	mMoveSpeed(100), mRotateSpeed(36), mMoveScale(0.0f), mRotScale(0.0f), mRotX(0), mRotY(0), mTranslateVector(Vector3::ZERO), mTranslateVector2(Vector3::ZERO)
{
	mWindow = 0;
	mDebugOverlay = 0;
	displayCameraDetails = false;
	statsOn = false;
	mTimeUntilNextToggle = 0.0f;
}

void PlayState::enter()
{
	// Assign variables
	mRoot = Root::getSingletonPtr();
	mWindow = mRoot->getAutoCreatedWindow();
	mSceneMgr = mRoot->getSceneManager("plitzScene");
	mDeviceManager = DeviceManager::getSingletonPtr();
	mDeviceManager->setBuffered(false);	// In-game uses unbuffered input
	mGUIManager = GUIManager::getSingletonPtr();
	mKeyboard = mDeviceManager->mKeyboard;
	mMouse = mDeviceManager->mMouse;

	// Set up DotScene Loader
	mDotSceneLoader = new DotSceneLoader();
	
	// Chat Box and SpriteManager
	mChatBox = ChatBox::getSingletonPtr();
	mSpriteManager=new Ogre2dManager;
	mSpriteManager->init(mSceneMgr, Ogre::RENDER_QUEUE_OVERLAY, true);

	// Event Flags
	mQuit = false;
	mDismiss = false;

	// NxOgre
	if (!nxWorld)
		nxWorld = new NxOgre::World();
	nxScene = nxWorld->createScene("nxScene", mSceneMgr, "gravity: yes, floor: yes");

	// Set up Debug Overlay
	mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");

	// Register GUI Items
	mGUIManager->registerScene(mSceneMgr);
	mGUIManager->setGUISheet(CEGUI::WindowManager::getSingletonPtr()->getWindow("Quit/Sheet"));
	mGUIManager->hide(); // defaults to hidden
	setupEventHandlers();
	
	createScene();
}

void PlayState::exit()
{
	// Reset Event Flags
	mQuit = false;
	mDismiss = false;

	// Clearing physics
	delete mBody;
	nxWorld->destroyScene("nxScene");
			
	// Removing Game GUI
	mChatBox->clear();
	mSpriteManager->end();
	delete mSpriteManager;

	// Switch off debug overlays
	displayCameraDetails = false;
	statsOn = false;
	mDebugOverlay->hide();

	// Clean up
	delete mDotSceneLoader;
	mSceneMgr->clearScene();
	mSceneMgr->destroyAllCameras();
	mRoot->getAutoCreatedWindow()->removeAllViewports();
}

void PlayState::pause()
{
}

void PlayState::resume()
{
	mViewport->setBackgroundColour(ColourValue(0.0, 0.0, 0.0));
	mDeviceManager->setBuffered(false);
}

bool PlayState::keyPressed(const OIS::KeyEvent &arg)
{
	if (arg.key == OIS::KC_ESCAPE || arg.key == OIS::KC_C)
		mDismiss = true;
	if (arg.key == OIS::KC_Q)
		mQuit = true;

	return true;
}

bool PlayState::keyReleased(const OIS::KeyEvent &arg)
{
	return true;
}

bool PlayState::mouseMoved( const OIS::MouseEvent &arg )
{
	CEGUI::System::getSingleton().injectMouseMove( arg.state.X.rel, arg.state.Y.rel );
	return true;
}

bool PlayState::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	CEGUI::System::getSingleton().injectMouseButtonDown(convertOISMouseButtonToCegui(id));
	return true;
}

bool PlayState::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	CEGUI::System::getSingleton().injectMouseButtonUp(convertOISMouseButtonToCegui(id));
	return true;
}

bool PlayState::frameStarted(const FrameEvent& evt)
{
	// Update toggle buffering time
	if (mTimeUntilNextToggle >= 0)
				mTimeUntilNextToggle -= evt.timeSinceLastFrame;
	
	// Updating debug overlay stats
	updateStats();

	// Update chatbox
	mChatBox->loop(evt.timeSinceLastFrame);

	// Draw sprites
	drawSprites();

	// Capture input devices
	if (mKeyboard) mKeyboard->capture();
	if (mMouse)	mMouse->capture();

	if ( !mDeviceManager->mUseBufferedInputMouse || !mDeviceManager->mUseBufferedInputKeys)
	{
		// Setup what is needed for immediate mouse/key movement
		// If this is the first frame, pick a speed
		if (evt.timeSinceLastFrame == 0)
		{
			mMoveScale = 1;
			mRotScale = 0.1;
		}
		// Otherwise scale movement units by time passed since last frame
		else
		{
			// Move about 100 units per second,
			mMoveScale = mMoveSpeed * evt.timeSinceLastFrame;
			// Take about 10 seconds for full rotation
			mRotScale = mRotateSpeed * evt.timeSinceLastFrame;
		}
		mRotX = 0;
		mRotY = 0;
		mTranslateVector = Vector3::ZERO;
		mTranslateVector2 = Vector3::ZERO;
	}

	if (!mDeviceManager->mUseBufferedInputMouse)
	{
		if (processUnbufferedMouseInput(evt) == false)
			return false;
	}

	if (!mDeviceManager->mUseBufferedInputKeys)
	{
		if (processUnbufferedKeyInput(evt) == false)
			return false;
	}

	// one of the input modes is immediate, so update the movement vector
	if (!mDeviceManager->mUseBufferedInputMouse || !mDeviceManager->mUseBufferedInputKeys)
	{
		moveCamera();
	}

	return true;
}

bool PlayState::frameEnded(const FrameEvent& evt)
{
	if (mQuit)
		changeState(NetworkState::getInstance()); // Return to NET STATE
	if (mDismiss)
	{	
		mGUIManager->hide();
		mDeviceManager->setBuffered(false);
		mDismiss = false;
	}
	return true;
}

void PlayState::createScene()
{
	// Load the scene
	mDotSceneLoader->parseDotScene("SpaceDock.scene","Plitzkrieg",mSceneMgr, nxScene);

	// Grab default camera and assign viewport
	mCamera = mSceneMgr->getCamera("SD_Camera");	
	mViewport = mRoot->getAutoCreatedWindow()->addViewport(mCamera);	
	mViewport->setBackgroundColour(ColourValue(0.0, 0.0, 0.0));
	mCamera->setAspectRatio(Real(mViewport->getActualWidth()) / Real(mViewport->getActualHeight()));

	// NxOgre loads the player
	mBody = nxScene->createBody("myCube;Cube.mesh", new CubeShape(1,1,1), Vector3(0, 100, 0), "mass: 50, node: myCube");
}

bool PlayState::processUnbufferedMouseInput(const FrameEvent& evt)
{
	using namespace OIS;

	// Rotation factors
	const MouseState &ms = mMouse->getMouseState();
	mRotX = Degree(-ms.X.rel * 0.13);
	mRotY = Degree(-ms.Y.rel * 0.13);
	mTranslateVector2.z  = ms.Z.rel * 0.13;
	
	return true;
}

bool PlayState::processUnbufferedKeyInput(const FrameEvent& evt)
{
	using namespace OIS;

	// Player 1 Keys
	if (mKeyboard->isKeyDown(KC_A))
		mBody->addForce(Vector3(-1000,0, 0));  // Move char WEST
		//mCharacter->addMovement(Character::DR_StepRight);
	if (mKeyboard->isKeyDown(KC_D))
		mBody->addForce(Vector3(1000,0, 0)); // Move char EAST
		//mCharacter->addMovement(Character::DR_StepLeft);
	if (mKeyboard->isKeyDown(KC_W))
		mBody->addForce(Vector3(0,0, -1000)); // Move char NORTH
		//mCharacter->addMovement(Character::DR_Forward);
	if (mKeyboard->isKeyDown(KC_S))
		mBody->addForce(Vector3(0,0, 1000)); // Move char SOUTH
		//mCharacter->addMovement(Character::DR_Backward);
	if (mKeyboard->isKeyDown(KC_SPACE))
		//mCharacter->addMovement(Character::DR_Jump_Up);
		mBody->addForce(Vector3(0, 1000, 0));
		
	// Player 2 Keys
	if (mKeyboard->isKeyDown(KC_J))
		mTranslateVector2.x = -mMoveScale; // Move cam WEST
	if (mKeyboard->isKeyDown(KC_L))
		mTranslateVector2.x = mMoveScale; // Move cam EAST
	if (mKeyboard->isKeyDown(KC_I))
		mTranslateVector2.y = mMoveScale; // Move cam NORTH
	if (mKeyboard->isKeyDown(KC_K))
		mTranslateVector2.y = -mMoveScale; // Move cam SOUTH

	// Summon quit menu
	if (mKeyboard->isKeyDown(KC_ESCAPE))
	{
		if (!mGUIManager->isDisplayed())
		{
			mGUIManager->show();
			mDeviceManager->setBuffered(true);
		}
	}

	// TODO: Toggle Kills
	if (mKeyboard->isKeyDown(KC_TAB))
		pushState(PauseState::getInstance()); // Move to PAUSE STATE

	// ChatBox keys
	if (mKeyboard->isKeyDown(KC_Y) && mTimeUntilNextToggle <= 0)
	{
		mChatBox->addText("You pressed the 'Y' Key in Play State.");
		mTimeUntilNextToggle = 0.3;
	}
	if (mKeyboard->isKeyDown(KC_U) && mTimeUntilNextToggle <= 0)
	{
		mChatBox->addText("You pressed the 'U' Key in Play State.");
		mTimeUntilNextToggle = 0.3;
	}

	// Toggle Debug Overlay
	if (mKeyboard->isKeyDown(KC_F1) && mTimeUntilNextToggle <= 0 && Config::getInstance()->cfg_debugOn)
	{
		statsOn = !statsOn;
		mTimeUntilNextToggle = 0.5;
		if (statsOn) mDebugOverlay->show();
		else mDebugOverlay->hide();
	}

	/*if (mInputDevice->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0)
	{
		std::ostringstream ss;
		ss << "screenshot_" << ++mNumScreenShots << ".png";
		mWindow->writeContentsToFile(ss.str());
		mTimeUntilNextToggle = 0.5;
		mDebugText = "Saved: " + ss.str();
	}*/

	/*if (mInputDevice->isKeyDown(KC_R) && mTimeUntilNextToggle <=0)
	{
		mSceneDetailIndex = (mSceneDetailIndex+1)%3 ;
		switch(mSceneDetailIndex) {
				case 0 : mCamera->setPolygonMode(PM_SOLID) ; break ;
				case 1 : mCamera->setPolygonMode(PM_WIREFRAME) ; break ;
				case 2 : mCamera->setPolygonMode(PM_POINTS) ; break ;
		}
		mTimeUntilNextToggle = 0.5;
	}*/
	
	if (mKeyboard->isKeyDown(KC_P) && mTimeUntilNextToggle <= 0) // Toggle display camera pos
	{
		displayCameraDetails = !displayCameraDetails;
		mTimeUntilNextToggle = 0.5;
		if (!displayCameraDetails)
			mDebugText = "";
	}

	if (displayCameraDetails)
	{
		// Print camera details
		mDebugText = "P: " + StringConverter::toString(mCamera->getDerivedPosition()) +
			" " + "O: " + StringConverter::toString(mCamera->getDerivedOrientation());
	}
	
	return true;
}

void PlayState::moveCamera()
{
	// Make all the changes to the camera
	// Note that YAW direction is around a fixed axis (freelook style) rather than a natural YAW (e.g. airplane)
	//playerNode->translate(mTranslateVector);
	
	mCamera->yaw(mRotX);
	mCamera->pitch(mRotY);
	mCamera->moveRelative(mTranslateVector2);
	//mCamera->lookAt(playerNode->getPosition());
	//mCamera->moveRelative(mTranslateVector);
}

void PlayState::drawSprites()
{
	mSpriteManager->spriteBltFull("healthbar.png", -0.94140625, 0.921875, -0.431640625, 0.6770833);
	mSpriteManager->spriteBltFull("trinity.png", 0.685546875, 0.902173913, 0.9375, 0.543478261);
}

void PlayState::setupEventHandlers()
{
	CEGUI::WindowManager *wmgr = CEGUI::WindowManager::getSingletonPtr();
	CEGUI::Window *quit = wmgr->getWindow((CEGUI::utf8*)"Quit/QuitButton");
	quit->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&PlayState::handleQuit, this));
    CEGUI::Window *cancel = wmgr->getWindow((CEGUI::utf8*)"Quit/CancelButton");
	cancel->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&PlayState::handleCancel, this));
}

bool PlayState::handleQuit(const CEGUI::EventArgs& e)
{
	mQuit = true;
	return true;
}

bool PlayState::handleCancel(const CEGUI::EventArgs& e)
{
	mDismiss = true;
	return true;
}

	//if (mKeyboard->isKeyDown(KC_A))
	//	mTranslateVector.x = -mMoveScale; // Move char EAST
	//if (mKeyboard->isKeyDown(KC_D))
	//	mTranslateVector.x = mMoveScale; // Move char WEST
	//if (mKeyboard->isKeyDown(KC_W))
	//	mTranslateVector.y = mMoveScale; // Move char NORTH
	//if (mKeyboard->isKeyDown(KC_S))
	//	mTranslateVector.y = -mMoveScale; // Move char SOUTH

	/*ANIMATION*/
	//myAnimState->addTime(evt.timeSinceLastFrame * 4);
	/*
	// Set Animation Settings
	Animation::setDefaultInterpolationMode(Animation::IM_LINEAR);
    Animation::setDefaultRotationInterpolationMode(Animation::RIM_LINEAR);
	*/

	//Ogre::AnimationState* myAnimState;
	/*Entity* gus = mSceneMgr->createEntity("paperman", "PaperMan.mesh");

	SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("gusnode");
	headNode->attachObject(gus);
	headNode->setPosition(Vector3(0.0f,0.0f,0.0f));
	headNode->scale(Vector3(10.0f,10.0f,10.0f));*/

	/*myAnimState = gus->getAnimationState("Walk");
	myAnimState->setEnabled(true);
	myAnimState->setLoop(true);*/

	/*Ogre::Entity* mEnt = mSceneMgr->getEntity("myCube-0");
	Ogre::SceneNode* mNode = mSceneMgr->getSceneNode("myCube");

	NameValuePairList pairList;
	pairList["numberOfChains"] = "2";
	pairList["maxElements"] = "80";
	RibbonTrail* trail = static_cast<RibbonTrail*>(mSceneMgr->createMovableObject("1", "RibbonTrail", &pairList));
    trail->setMaterialName("Examples/LightRibbonTrail");
    trail->setTrailLength(400);

    mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(trail);
      
    trail->setInitialColour(0, 1.0, 0.8, 0);
    trail->setColourChange(0, 0.5, 0.5, 0.5, 0.5);
    trail->setInitialWidth(0, 5);

	trail->addNode(mNode);*/